package com.qumitech.visualnovel.service.player.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.qumitech.visualnovel.component.exception.BusinessException;
import com.qumitech.visualnovel.component.util.CommonUtil;
import com.qumitech.visualnovel.component.util.HttpInvokeUtil;
import com.qumitech.visualnovel.model.common.BaseRsp;
import com.qumitech.visualnovel.model.common.ResultEnum;
import com.qumitech.visualnovel.model.common.BaseResponse;
import com.qumitech.visualnovel.model.dto.*;
import com.qumitech.visualnovel.controller.vo.player.AccountForbiddenVo;
import com.qumitech.visualnovel.controller.vo.player.AccountPropertyVo;
import com.qumitech.visualnovel.controller.vo.player.UserResetRecordVO;
import com.qumitech.visualnovel.controller.vo.player.UserResetVO;
import com.qumitech.visualnovel.controller.vo.player.AccountPreviewVO;
import com.qumitech.visualnovel.datasource.console.AccountLoginMapper;
import com.qumitech.visualnovel.datasource.console.AccountRegisterMapper;
import com.qumitech.visualnovel.datasource.editor.BookDAOMapper;
import com.qumitech.visualnovel.datasource.editor.ChapterDAOMapper;
import com.qumitech.visualnovel.datasource.editor.FrameDAOMapper;
import com.qumitech.visualnovel.datasource.app.GuardMapper;
import com.qumitech.visualnovel.datasource.app.NobleMapper;
import com.qumitech.visualnovel.datasource.app.AccountMapper;
import com.qumitech.visualnovel.datasource.app.UserCurrencyPropDOMapper;
import com.qumitech.visualnovel.datasource.app.UserBookDOMapper;
import com.qumitech.visualnovel.datasource.app.ISOCountryDAOMapper;
import com.qumitech.visualnovel.model.dao.app.Account;
import com.qumitech.visualnovel.model.dao.app.UserCurrencyPropDO;
import com.qumitech.visualnovel.model.dao.app.UserBookDO;
import com.qumitech.visualnovel.model.dao.app.ISOCountryDAO;
import com.qumitech.visualnovel.model.dao.console.AccountLogin;
import com.qumitech.visualnovel.model.dao.console.AccountRegister;
import com.qumitech.visualnovel.model.dao.console.UserInfo;
import com.qumitech.visualnovel.model.dao.editor.BookDAO;
import com.qumitech.visualnovel.model.dao.editor.FrameDAO;
import com.qumitech.visualnovel.model.common.UrlConstant;
import com.qumitech.visualnovel.pubdef.dto.user.UserGuardRecordDTO;
import com.qumitech.visualnovel.pubdef.dto.user.UserNobleRecordDTO;
import com.qumitech.visualnovel.service.player.PlayerInfoService;
import com.qumitech.visualnovel.service.account.UserInfoService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Array;
import java.util.*;


@Service
public class PlayerInfoServiceImpl implements PlayerInfoService {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private static final String PLAYER_LIST_URL = "/account/admin/information";
    private static final String PLAYER_UPDATE_URL = "/me/console/information";
    private static final String FORBIDDEN_ADD_URL = "/user/forbidden";
    private static final String FORBIDDEN_DEL_URL = "/user/forbidden/%s";
    private static final String CREATE_VOICE_URL = "/voiceroom/anchor";

    private static final String SHOWID_CONVERT_UID = "/account/admin/query/uid";
    private static final String USERNAME_CONVERT_UID = "/account/admin/query/username";
    private static final String PHONE_NUM_CONVERT_UID = "/account/admin/query/phone_num";
    private static final String APP_CHANNEL_CONVERT_UID = "/account/admin/query/app_channel";
    private static final String USER_GET_RESET = "/admin/usercontent/%d/reset";
    private static final String USER_PUT_RESET = "/admin/usercontent/%d/reset";
    private static final String USER_GET_RESET_RECORD = "/admin/usercontent/reset_record";

    @Autowired
    UserInfoService userInfoService;

    @Autowired
    AccountRegisterMapper accountRegisterMapper;

    @Autowired
    AccountLoginMapper accountLoginMapper;

    @Autowired
    GuardMapper guardMapper;

    @Autowired
    NobleMapper nobleMapper;

    @Autowired
    AccountMapper accountMapper;

    @Autowired
    UserCurrencyPropDOMapper userCurrencyPropDOMapper;

    @Autowired
    UserBookDOMapper userBookDOMapper;

    @Autowired
    BookDAOMapper bookDAOMapper;

    @Autowired
    ChapterDAOMapper chapterDAOMapper;

    @Autowired
    FrameDAOMapper frameDAOMapper;

    @Autowired
    ISOCountryDAOMapper isoCountryDAOMapper;

    private Integer getRootFrameIndex(Long frameId) {
        FrameDAO frameDAO = frameDAOMapper.selectByPrimaryKey(frameId);
        if (frameDAO == null) {
            return null;            //小说重新发布后，之前的帧可能被删掉
        } else {
            Long parentId = frameDAO.getParentId();
            return parentId==null ? frameDAO.getIndex() : this.getRootFrameIndex(parentId);
        }
    }

    private BookNumberDTO getUserReadNumber(Long uid, Long bookId) {
        BookNumberDTO readNum = userBookDOMapper.getUserReadNum(uid, bookId);
        if (readNum == null) return readNum;

        Long frameId = readNum.getNumber2();
        Integer rootFrameIndex = this.getRootFrameIndex(frameId);
        readNum.setNumber2(rootFrameIndex==null ? 0l : rootFrameIndex.longValue());
        return readNum;
    }

    private Map<Long, AccountCurrencyDTO> getUserCurrencyMap(List<Long> uidList) {
        Map<Long, AccountCurrencyDTO> result = new HashMap<>();
        if (uidList==null || uidList.size()==0) {
            return result;
        }

        List<UserCurrencyPropDO> list = userCurrencyPropDOMapper.list(uidList);
        list.forEach(item -> {
            AccountCurrencyDTO accountCurrencyDTO = result.get(item.getUid());
            if (accountCurrencyDTO == null) {
                accountCurrencyDTO = new AccountCurrencyDTO(item.getUid(), 0L, 0L);
                result.put(item.getUid(), accountCurrencyDTO);
            }
            if (item.getPropId() == 1) {
                accountCurrencyDTO.setDiamonds(item.getAmount());
            } else if (item.getPropId() == 2) {
                accountCurrencyDTO.setKeys(item.getAmount());
            }
        });
        return result;
    }

    @Override
    public JSONObject getAccountList(AccountInfoQueryDTO param) {
        String orderBy = param.getOrderBy();
        if (orderBy!=null && orderBy.equals("registerTime")) {
            param.setOrderBy("a.create_time");
        }

        int total = accountMapper.total(param);
        List<AccountInfoResponseDTO> list = accountMapper.list(param);

        List<Long> uidList = new ArrayList<>();
        list.forEach(item -> { uidList.add(item.getUid()); });
        Map<Long, AccountCurrencyDTO> currencyMap = this.getUserCurrencyMap(uidList);
        list.forEach(item -> {
            AccountCurrencyDTO accountCurrencyDTO = currencyMap.get(item.getUid());
            if (accountCurrencyDTO != null) {
                item.setDiamonds(accountCurrencyDTO.getDiamonds());
                item.setKeys(accountCurrencyDTO.getKeys());
            }
        });

        return new BaseResponse(list, total);
    }

    @Override
    public JSONObject getAccountDetail(String accountId) {
        AccountDetailDTO accountDetailDTO = accountMapper.getDetail(accountId);

        List<Long> uidList = new ArrayList<>();
        uidList.add(Long.valueOf(accountId));
        Map<Long, AccountCurrencyDTO> currencyMap = this.getUserCurrencyMap(uidList);
        AccountCurrencyDTO accountCurrencyDTO = currencyMap.get(Long.valueOf(accountId));
        if (accountDetailDTO!=null && accountCurrencyDTO!=null) {
            accountDetailDTO.setDiamonds(accountCurrencyDTO.getDiamonds());
            accountDetailDTO.setKeys(accountCurrencyDTO.getKeys());
        }

        UserBookDO userBookDO = userBookDOMapper.selectRecord(Long.valueOf(accountId));
        if (accountDetailDTO!=null && userBookDO!=null) {
            accountDetailDTO.setLastSeenTime(userBookDO.getReadTime());
        }

        return new BaseResponse(accountDetailDTO);
    }

    @Override
    public JSONObject accountLocationOptions() {
        List<ISOCountryDAO> options = isoCountryDAOMapper.getLocationOptions();

        JSONArray result = new JSONArray();
        options.forEach(item -> {
            JSONObject jItem = new JSONObject();
            jItem.put("id", item.getAlpha2Code());
            jItem.put("name", item.getEnName());
            result.add(jItem);
        });
        return new BaseResponse(result);
    }

    @Override
    public JSONObject getAccountPreview(String accountId) {
        JSONObject resObject = new JSONObject();
        JSONObject postData = JSONObject.parseObject("{\"list_uid\":["+accountId+"]}");
        String body = HttpInvokeUtil.httpPostJson(UrlConstant.SERVER_URL + PLAYER_LIST_URL,
                JSONObject.toJSONString(postData), UrlConstant.DefaultEncode, null);
        BaseRsp rsp = JSONObject.parseObject(body, BaseRsp.class);
        if (rsp == null || rsp.getStatus() != ResultEnum.SUCCESS.getState()) {
            throw new BusinessException(ResultEnum.FAILURE);
        }
        JSONObject data = (JSONObject)rsp.getData();
        JSONArray accountList = data.getJSONArray("account_list");
        for(int i=0;i<accountList.size();i++) {
            String jsonStr = JSON.toJSONString(accountList.getJSONObject(i));
            AccountPreviewVO vo = JSON.parseObject(jsonStr, AccountPreviewVO.class);
            resObject.put("code", 0);
            resObject.put("msg","success");
            resObject.put("data", vo);
        }
        return resObject;
    }

    @Override
    public JSONObject accountUpdate(AccountPropertyVo accountPropertyVo) {
        logger.info("postData:" + JSONObject.toJSONString(accountPropertyVo));
        String body = HttpInvokeUtil.httpPutJson(UrlConstant.SERVER_URL + PLAYER_UPDATE_URL,
                JSONObject.toJSONString(accountPropertyVo),UrlConstant.DefaultEncode, null);
        BaseRsp rsp = JSONObject.parseObject(body, BaseRsp.class);
        if (rsp == null || rsp.getStatus() != ResultEnum.SUCCESS.getState()) {
            throw new BusinessException(ResultEnum.FAILURE);
        }
        logger.info("body:" + rsp.toString());
        return new BaseResponse<>(0, "success");
    }

    @Override
    public JSONObject accountForbiddenAdd(AccountForbiddenVo accountForbiddenVo) {
        logger.info("postData:" + JSON.toJSONStringWithDateFormat(accountForbiddenVo,
                "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteDateUseDateFormat));
        String body = HttpInvokeUtil.httpPostJson(UrlConstant.SERVER_URL + FORBIDDEN_ADD_URL,
                JSON.toJSONStringWithDateFormat(accountForbiddenVo, "yyyy-MM-dd HH:mm:ss",
                        SerializerFeature.WriteDateUseDateFormat),UrlConstant.DefaultEncode, null);
        BaseRsp rsp = JSONObject.parseObject(body, BaseRsp.class);
        if (rsp == null || rsp.getStatus() != ResultEnum.SUCCESS.getState()) {
            throw new BusinessException(ResultEnum.FAILURE);
        }
        logger.info("body:" + rsp.toString());
        return new BaseResponse<>(0, "success");
    }

    @Override
    public JSONObject accountForbiddenDel(String forbiddenId) {
        String url = UrlConstant.SERVER_URL + String.format(FORBIDDEN_DEL_URL, forbiddenId);
        logger.info("url:" + url);
        String body = HttpInvokeUtil.httpDelete(url, new HashMap<>());
        BaseRsp rsp = JSONObject.parseObject(body, BaseRsp.class);
        if (rsp == null || rsp.getStatus() != ResultEnum.SUCCESS.getState()) {
            throw new BusinessException(ResultEnum.FAILURE);
        }
        logger.info("body:" + rsp.toString());
        return new BaseResponse<>(0, "success");
    }

    @Override
    public JSONObject createVoiceRoom(String accountId) {
        JSONObject resObject = new JSONObject();
        String url = UrlConstant.SERVER_URL + CREATE_VOICE_URL;
        logger.info("url:" + url);
        JSONObject postData = new JSONObject();
        postData.put("uid", Integer.parseInt(accountId));
        postData.put("type", 1);
        String body = HttpInvokeUtil.httpPostJson(url, JSONObject.toJSONString(postData),
                UrlConstant.DefaultEncode, null);
        BaseRsp rsp = JSONObject.parseObject(body, BaseRsp.class);
        if (rsp == null || rsp.getStatus() != ResultEnum.SUCCESS.getState()) {
            throw new BusinessException(ResultEnum.FAILURE);
        }
        logger.info("body:" + rsp.toString());
        return new BaseResponse<>(0, "success");

    }


    @Override
    public JSONObject getRewardList(String appId, String startTime, String endTime, int offset, int limit) {
        List<String> tableList = CommonUtil.getTableListFromDate(startTime, endTime, "account_login");
        List<HashMap<String, Object>> results = accountLoginMapper.getRewordAccount(tableList,appId,startTime,endTime);
        int count = results.size();
        JSONArray listArray = new JSONArray();

        for(int i = offset;i<offset+limit&&i<results.size();i++){
            listArray.add(Integer.parseInt(results.get(i).get("account_id").toString()));
        }

        JSONObject postData = new JSONObject();
        postData.put("list_uid", listArray);
        logger.info("postData:" + JSONObject.toJSONString(postData));
        String body = HttpInvokeUtil.httpPostJson(UrlConstant.SERVER_URL + PLAYER_LIST_URL,
                JSONObject.toJSONString(postData),UrlConstant.DefaultEncode, null);
        BaseRsp rsp = JSONObject.parseObject(body, BaseRsp.class);
        if (rsp == null || rsp.getStatus() != ResultEnum.SUCCESS.getState()) {
            throw new BusinessException(ResultEnum.FAILURE);
        }
        logger.info("body:" + rsp.toString());
        JSONObject data = (JSONObject)rsp.getData();
        JSONArray accountList = data.getJSONArray("account_list");
        JSONArray resultArray = new JSONArray();
        for(int i = 0; i < accountList.size(); i++) {
            JSONObject item = accountList.getJSONObject(i);
            JSONObject tempItem = new JSONObject();
            tempItem.put("account_id", item.get("uid"));
            tempItem.put("present_id", item.get("present_id"));
            tempItem.put("user_name", item.getString("user_name"));
            resultArray.add(tempItem);
        }
        return new BaseResponse<>(resultArray,count);
    }

    @Override
    public int getAccountLoginCount(Integer accountId, String startTime, String endTime, String appId) {
        String[] stringTime = CommonUtil.setStartTime(startTime, endTime);
        List<String> tableList = CommonUtil.getTableListFromDate(stringTime[0], stringTime[1],
                "account_login");
        return accountLoginMapper.getCount(accountId, startTime, endTime, appId, tableList);
    }

    @Override
    public List<AccountLogin> getAccountLoginList(Integer accountId, String startTime, String endTime, String appId,
                                                  int offset, int limit, String orderType, String orderBy) {
        String[] stringTime = CommonUtil.setStartTime(startTime, endTime);
        List<String> tableList = CommonUtil.getTableListFromDate(stringTime[0], stringTime[1],
                "account_login");
        List<AccountLogin> accountLoginList = accountLoginMapper.getList(accountId, startTime, endTime, appId, offset, limit,
                orderType, orderBy, tableList);
        for (AccountLogin accountLogin: accountLoginList) {
            Integer loginType = accountLogin.getLoginType();
            if (loginType!=null) {
                switch (loginType) {
                    case 0:
                        accountLogin.setLoginTypeName("by_sms");continue;
                    case 1:
                        accountLogin.setLoginTypeName("by_passwd");continue;
                    case 2:
                    case 4:
                    case 5:
                    case 6:
                        accountLogin.setLoginTypeName("by_wechat");continue;
                    case 3:
                    case 7:
                    case 8:
                        accountLogin.setLoginTypeName("by_qq");continue;
                    case 9:
                        accountLogin.setLoginTypeName("by_oneclick");continue;
                    case 10:
                    case 11:
                        accountLogin.setLoginTypeName("by_appleid");continue;
                    case 12:
                        accountLogin.setLoginTypeName("by_facebook");continue;
                    case 13:
                        accountLogin.setLoginTypeName("by_google");continue;
                    case 14:
                        accountLogin.setLoginTypeName("by_twitter");
                }
            }
        }
        return accountLoginList;
    }

    @Override
    public int getAccountRegisterCount(Integer accountId, String startTime, String endTime, String appId) {
        return accountRegisterMapper.getCount(accountId, startTime, endTime, appId, null, null);
    }

    @Override
    public List<AccountRegister> getAccountRegisterList(Integer accountId, String startTime, String endTime,
                                                        String appId, int offset, int limit, String orderType,
                                                        String orderBy) {
        return accountRegisterMapper.getList(accountId, startTime, endTime,null, appId, null,offset, limit,
                orderType, orderBy);
    }

    @Override
    public BaseRsp getUserReset(Long uid) {
        String url = UrlConstant.SERVER_URL + String.format(USER_GET_RESET, uid);
        String body = HttpInvokeUtil.httpGet(url, new HashMap<>(16));
        return JSONObject.parseObject(body, BaseRsp.class);
    }

    @Override
    public BaseRsp resetUserReset(UserResetVO userResetVO) {
        Subject subject = SecurityUtils.getSubject();
        UserInfo activeUser = (UserInfo) subject.getPrincipal();
        userResetVO.setOperatorId(activeUser.getUid().longValue());
        userResetVO.setOperator(activeUser.getUsername());
        String url = UrlConstant.SERVER_URL + String.format(USER_PUT_RESET, userResetVO.getUid());
        String req = JSONObject.toJSONString(userResetVO);
        String body = HttpInvokeUtil.httpPutJson(url,req,UrlConstant.DefaultEncode,null);
        return JSONObject.parseObject(body, BaseRsp.class);
    }

    @Override
    public BaseRsp getResetRecords(UserResetRecordVO resetRecordVO) {
        String url = UrlConstant.SERVER_URL + USER_GET_RESET_RECORD;
        String body = HttpInvokeUtil.httpGetT(url, resetRecordVO);
        return JSONObject.parseObject(body, BaseRsp.class);
    }

    @Override
    public BaseRsp listGuard(Long fromId, Long toId, String startTime, String endTime, String orderBy,
                             String orderType, int offset, int limit) {
        List<UserGuardRecordDTO> result = guardMapper.listGuard(fromId, toId, startTime, endTime, orderBy, orderType, offset, limit);
        int count = guardMapper.countGuard(fromId, toId, startTime, endTime);
        return new BaseRsp(ResultEnum.SUCCESS,  ImmutableMap.of("items", result, "count", count));

    }

    @Override
    public BaseRsp listNoble(Long uid, Long level, String startTime, String endTime, String orderBy,
                             String orderType, int offset, int limit) {
        List<UserNobleRecordDTO> result = nobleMapper.listNoble(uid, level, startTime, endTime, orderBy, orderType, offset, limit);
        int count = nobleMapper.countNoble(uid, level, startTime, endTime);
        return new BaseRsp(ResultEnum.SUCCESS,  ImmutableMap.of("items", result, "count", count));

    }

    @Override
    public BaseRsp getNobleConfig() {
        List<HashMap<Integer, String>> result = nobleMapper.getNobleConfig();
        return new BaseRsp(ResultEnum.SUCCESS, result);
    }

    @Override
    public JSONObject bookReadList(BookReadQueryDTO param) {
        Long uid = param.getUid();
        List<Long> bookIdList = new ArrayList<>();
        Map<Long, BookDAO> bookInfoMap = bookDAOMapper.getBookMapByTitle(param.getBookTitle());
        bookInfoMap.forEach((bookId, item) -> { bookIdList.add(bookId); });
        param.setBookIdList(bookIdList);

        int total = userBookDOMapper.total(param);
        List<UserBookDO> list = userBookDOMapper.list(param);

        bookIdList.clear();
        list.forEach(item -> { bookIdList.add(item.getBookId()); });

        Map<Long, BookNumberDTO> totalChapterMap = bookIdList.isEmpty() ? new HashMap<>() : chapterDAOMapper.getTotalChapterMap(bookIdList);
        Map<String, BookNumberDTO> totalFrameMap = bookIdList.isEmpty() ? new HashMap<>() : chapterDAOMapper.getTotalFrameMap(bookIdList);
        Map<String, BookNumberDTO> bookReadCostMap = userBookDOMapper.getBookReadCostMap(uid, bookIdList);

        JSONArray result = new JSONArray();
        list.forEach(item -> {
            JSONObject jItem = new JSONObject();
            Long bookId = item.getBookId();
            jItem.put("bookId", bookId);
            jItem.put("lastSeenTime", item.getReadTime());

            BookDAO bookDAO = bookInfoMap.get(bookId);
            jItem.put("title", bookDAO==null ? "" : bookDAO.getTitle());

            BookNumberDTO userRead = this.getUserReadNumber(uid, bookId);
            jItem.put("readChapter", userRead==null ? 0 : userRead.getNumber1());
            jItem.put("readFrame", userRead==null ? 0 : userRead.getNumber2());

            BookNumberDTO totalChapter = totalChapterMap.get(bookId);
            jItem.put("totalChapter", totalChapter==null ? 0 : totalChapter.getNumber1());

            BookNumberDTO totalFrame = totalFrameMap.get(bookId + "_" + jItem.getInteger("readChapter"));
            jItem.put("totalFrame", totalFrame==null ? 0 : totalFrame.getNumber1());

            BookNumberDTO diamondsCost = bookReadCostMap.get(bookId + "_0_1");
            jItem.put("diamonds", diamondsCost==null ? 0 : diamondsCost.getNumber1());

            BookNumberDTO keysCost = bookReadCostMap.get(bookId + "_0_2");
            jItem.put("keys", keysCost==null ? 0 : keysCost.getNumber1());

            result.add(jItem);
        });

        return new BaseResponse(result, total);
    }
}
